/*
 * Copyright 2000-2006 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.intellij.gwt.i18n;

import com.intellij.gwt.GwtConfiguration;
import com.intellij.lang.properties.psi.PropertiesFile;
import com.intellij.lang.properties.psi.Property;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.psi.*;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.javadoc.PsiDocTagValue;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

/**
 * @author nik
 */
public class GwtI18nManagerImpl extends GwtI18nManager implements ProjectComponent {
  private static final Property[] EMPTY_PROPERTIES_ARRAY = new Property[0];
  private static final PropertiesFile[] EMPTY_PROPERTIES_FILE_ARRAY = new PropertiesFile[0];
  private PsiManager myPsiManager;


  public GwtI18nManagerImpl(PsiManager psiManager) {
    myPsiManager = psiManager;
  }

  public void projectOpened() {
  }

  public void projectClosed() {
  }

  @NonNls
  @NotNull
  public String getComponentName() {
    return "GwtI18nManager";
  }

  public void initComponent() {
  }

  public void disposeComponent() {
  }


  public boolean isConstantsOrMessagesInterface(@NotNull PsiClass aClass) {
    if (!isValidGwtConfiguration() || !aClass.isInterface()) {
      return false;
    }

    final GlobalSearchScope scope = aClass.getResolveScope();
    final PsiClass constantsClass = myPsiManager.findClass(GwtI18nUtil.CONSTANTS_INTERFACE_NAME, scope);
    if (constantsClass != null && aClass.isInheritor(constantsClass, true)) {
      return true;
    }
    final PsiClass messagesClass = myPsiManager.findClass(GwtI18nUtil.MESSAGES_INTERFACE_NAME, scope);
    return messagesClass != null && aClass.isInheritor(messagesClass, true);
  }

  protected boolean isValidGwtConfiguration() {
    return GwtConfiguration.getInstance().isValidConfiguration();
  }

  @NotNull
  public PropertiesFile[] getPropertiesFiles(@NotNull PsiClass anInterface) {
    final PsiDirectory psiDirectory = anInterface.getContainingFile().getContainingDirectory();
    if (psiDirectory == null || !isConstantsOrMessagesInterface(anInterface)) {
      return EMPTY_PROPERTIES_FILE_ARRAY;
    }

    final PsiFile[] psiFiles = psiDirectory.getFiles();
    List<PropertiesFile> files = new ArrayList<PropertiesFile>();
    for (PsiFile psiFile : psiFiles) {
      if (psiFile instanceof PropertiesFile) {
        final PropertiesFile propertiesFile = (PropertiesFile)psiFile;
        final String fileName = propertiesFile.getName();
        final String interfaceName = anInterface.getName();
        if (isFileNameForInterfaceName(fileName, interfaceName)) {
          files.add(propertiesFile);
        }
      }
    }
    return files.toArray(new PropertiesFile[files.size()]);
  }

  private static boolean isFileNameForInterfaceName(final @Nullable String fileName, final @Nullable String interfaceName) {
    return fileName != null && interfaceName != null &&
        (fileName.equals(interfaceName + "." + StdFileTypes.PROPERTIES.getDefaultExtension()) || fileName.startsWith(interfaceName + "_"));
  }

  @Nullable
  public PsiClass getPropertiesInterface(@NotNull PropertiesFile file) {
    final String fileName = file.getName();
    final PsiDirectory directory = file.getContainingDirectory();
    if (directory == null || fileName == null || !isValidGwtConfiguration()) {
      return null;
    }

    for (PsiFile psiFile : directory.getFiles()) {
      if (psiFile instanceof PsiJavaFile) {
        final PsiClass[] psiClasses = ((PsiJavaFile)psiFile).getClasses();
        for (PsiClass psiClass : psiClasses) {
          if (isFileNameForInterfaceName(fileName, psiClass.getName()) && isConstantsOrMessagesInterface(psiClass)) {
            return psiClass;
          }
        }
      }
    }

    return null;
  }

  public String getPropertyName(@NotNull PsiMethod method) {
    final PsiDocComment docComment = method.getDocComment();
    if (docComment != null) {
      final PsiDocTag tag = docComment.findTagByName("gwt.key");
      if (tag != null) {
        final PsiDocTagValue psiDocTagValue = tag.getValueElement();
        if (psiDocTagValue != null) {
          final String text = psiDocTagValue.getText();
          if (text != null) {
            return text;
          }
        }
      }
    }
    return method.getName();
  }

  @NotNull
  public Property[] getProperties(@NotNull PsiMethod method) {
    final PsiClass aClass = method.getContainingClass();
    if (aClass == null) {
      return EMPTY_PROPERTIES_ARRAY;
    }

    final PropertiesFile[] files = getPropertiesFiles(aClass);
    if (files.length == 0) {
      return EMPTY_PROPERTIES_ARRAY;
    }

    final String propertyName = getPropertyName(method);
    List<Property> properties = new ArrayList<Property>();
    for (PropertiesFile file : files) {
      final Property property = file.findPropertyByKey(propertyName);
      if (property != null) {
        properties.add(property);
      }
    }
    return properties.toArray(new Property[properties.size()]);
  }

  @Nullable
  public PsiMethod getMethod(@NotNull Property property) {
    final PsiClass psiClass = getPropertiesInterface(property.getContainingFile());
    if (psiClass == null) {
      return null;
    }

    final PsiMethod[] psiMethods = psiClass.getMethods();
    for (PsiMethod psiMethod : psiMethods) {
      final String propertyName = getPropertyName(psiMethod);
      if (propertyName != null && propertyName.equals(property.getKey())) {
        return psiMethod;
      }
    }

    return null;
  }
}
